package com.monkey.netty.chapter1;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

/**
 * NiO服务端
 * 
 * @author monkeychen
 */
public class NioServer {

	private static Selector selector;

	public static void main(String[] args) throws IOException {
		// 打开服务端连接通道，用于监听客户端连接
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		// 设置通道为非阻塞
		serverSocketChannel.configureBlocking(false);
		// 绑定端口
		serverSocketChannel.bind(new InetSocketAddress(8888), 1024);
		// 打开多路复用器
		selector = Selector.open();
		// 把serverSocketChannel注册到多路复用器上，并且监听客户端连接事件
		serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
		// 启动Reactor线程，循环遍历selector上的事件
		new Thread(new ReactorThread()).start();
		System.out.println("服务端启动成功!");
	}

	private static class ReactorThread implements Runnable {
		@Override
		public void run() {
			while (true) {
				try {
					// 等待事件就绪，底层调用select函数，此处会阻塞
					selector.select();
					Iterator<SelectionKey> it = selector.selectedKeys().iterator();
					while (it.hasNext()) {
						SelectionKey selectionKey = it.next();
						it.remove();
						// 分发事件
						dispatch(selectionKey);
					}
				} catch (IOException e) {
					e.printStackTrace();
				}

			}
		}

		/**
		 * 事件分发
		 * 
		 * @param selectionKey
		 * @throws IOException
		 */
		private void dispatch(SelectionKey selectionKey) throws IOException {
			if (selectionKey.isAcceptable()) {
				System.out.println("有客户端连接!");
				// 处理客户端连接事件
				doAccept(selectionKey);
			} else if (selectionKey.isReadable()) {
				// 读取客户端数据
				doRead(selectionKey);
			}

		}

		/**
		 * 处理客户端连接事件
		 * 
		 * @param selectionKey
		 * @throws IOException
		 */
		private void doAccept(SelectionKey selectionKey) throws IOException {
			ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
			SocketChannel socketChannel = serverSocketChannel.accept();
			socketChannel.configureBlocking(false);
			socketChannel.register(selectionKey.selector(), SelectionKey.OP_READ);
		}

		/**
		 * 读取客户端数据
		 * 
		 * @param selectionKey
		 * @throws IOException
		 */
		private void doRead(SelectionKey selectionKey) throws IOException {
			SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
			ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
			// 前面我们已经将socketChannel设置为非阻塞，所以调用read方法的是不会阻塞线程
			int data = socketChannel.read(byteBuffer);
			if (data < 0) {
				selectionKey.cancel();
				socketChannel.close();
				System.out.println("客户端断开连接!");
			} else {
				String info = new String(byteBuffer.array(), "GBK");
				System.out.println(info);
			}
		}

	}
}
